/*
* Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved.
* gazelle is licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*     http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include <stdint.h>
#include <lwipgz_flow.h>
#include "lstack_conn_htable.h"
#include "lstack_sock_htable.h"
#include "lstack_rule.h"
#include "lstack_log.h"

static struct vdev_rule_reg_ops g_vdev_soft_rule_ops;
static inline void qtuple_to_listen_key(struct gazelle_quintuple *qtuple, struct sock_htable_key *key)
{
    key->proto = qtuple->protocol;
    key->port = qtuple->src_port;

    if (qtuple->protocol & GZ_ADDR_TYPE_V4) {
        key->ip[0] = qtuple->src_ip.u_addr.ip4.addr;
        key->ip[1] = 0;
        key->ip[2] = 0;
        key->ip[3] = 0;
    } else {
        key->ip[0] = qtuple->src_ip.u_addr.ip6.addr[0];
        key->ip[1] = qtuple->src_ip.u_addr.ip6.addr[1];
        key->ip[2] = qtuple->src_ip.u_addr.ip6.addr[2];
        key->ip[3] = qtuple->src_ip.u_addr.ip6.addr[3];
    }
}

static int soft_reg_listen_port(struct gazelle_quintuple *qtuple, uint16_t queue_id, enum reg_ring_type type)
{
    struct sock_htable_key key = {0};
    qtuple_to_listen_key(qtuple, &key);
    return sock_htable_add_key_data(&key, queue_id);
}

static int soft_unreg_listen_port(struct gazelle_quintuple *qtuple, uint16_t queue_id, enum reg_ring_type type)
{
    struct sock_htable_key key = {0};
    qtuple_to_listen_key(qtuple, &key);
    return sock_htable_del_key(&key, queue_id);
}

static int soft_reg_conn(struct gazelle_quintuple *qtuple, uint16_t queue_id, enum reg_ring_type type)
{
    return conn_htable_add_by_quintuple(qtuple, queue_id) != NULL ? 0 : -1;
}

static int soft_unreg_conn(struct gazelle_quintuple *qtuple, uint16_t queue_id, enum reg_ring_type type)
{
    conn_htable_del_by_quintuple(qtuple);
    return 0;
}

static int soft_find_conn(struct gazelle_quintuple *qtuple, uint16_t queue_id, enum reg_ring_type type)
{
    return conn_htable_get_queue_by_quintuple(qtuple, queue_id);
}
static int soft_find_listen_port(struct gazelle_quintuple *qtuple, uint16_t queue_id, enum reg_ring_type type)
{
    struct sock_htable_key key = {0};
    qtuple_to_listen_key(qtuple, &key);
    return sock_htable_get_by_min_conn(&key, queue_id, true);
}

void vdev_soft_rule_ops_init(void)
{
    g_vdev_soft_rule_ops.reg_listen_port = soft_reg_listen_port;
    g_vdev_soft_rule_ops.unreg_listen_port = soft_unreg_listen_port;
    g_vdev_soft_rule_ops.reg_conn = soft_reg_conn;
    g_vdev_soft_rule_ops.unreg_conn = soft_unreg_conn;
    g_vdev_soft_rule_ops.find_conn = soft_find_conn;
    g_vdev_soft_rule_ops.find_listen_port = soft_find_listen_port;

    vdev_rule_register(&g_vdev_soft_rule_ops);
}
